﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using com.eforceglobal.crux.web.wrokflows;
using com.eforceglobal.crux.web.wrokflows.DTOs;

namespace com.eforcegloabal.crux.web.wrokflows
{
    public class RoutingRuleManager
    {
        public List<string> GetUsersInRoutingRuleRoles(int workflowInstanceId, Dictionary<string, string> userRoles)
        {
            var workflowInstance = new WorkflowManager().GetWorkflowInstance(workflowInstanceId);
            var routingRule = ConfigParser.GetRoutingRuleForTheActivity(workflowInstance.WorkflowName, workflowInstance.ActivityName, workflowInstance.StateName);
            routingRule.WorkflowInstanceId = workflowInstanceId;
            return (from userRole in userRoles
                    let isUserInRoutingRuleRole = GetRoutingRuleValue(routingRule, userRole.Key, userRole.Value.Split(',').ToList())
                    where isUserInRoutingRuleRole
                    select userRole.Key).ToList();
        }
        public RoutingRule GetRoutingRuleForTheActivity(int workflowInstanceId)
        {
            var workflowInstance = new WorkflowManager().GetWorkflowInstance(workflowInstanceId);
            var routingRule = ConfigParser.GetRoutingRuleForTheActivity(workflowInstance.WorkflowName, workflowInstance.ActivityName, workflowInstance.StateName);
            routingRule.WorkflowInstanceId = workflowInstanceId;

            return routingRule;
        }
        public RoutingRule GetRoutingRuleForTheAction(int workflowInstanceId, string actionName)
        {
            var workflowInstance = new WorkflowManager().GetWorkflowInstance(workflowInstanceId);
            var routingRule = ConfigParser.GetRoutingRuleForTheAction(workflowInstance.WorkflowName, workflowInstance.ActivityName, actionName);
            routingRule.WorkflowInstanceId = workflowInstanceId;

            return routingRule;
        }

        public bool GetRoutingRuleValueForTheActivity(int workflowInstanceId, string user, List<string> roles)
        {
            var routingRule = GetRoutingRuleForTheActivity(workflowInstanceId);
            var ruleValue = GetRoutingRuleValue(routingRule, user, roles);
            return ruleValue;
        }
        public bool GetRoutingRuleValueForTheAction(int workflowInstanceId, string user, List<string> roles, string actionName)
        {
            var routingRule = GetRoutingRuleForTheAction(workflowInstanceId, actionName);
            var ruleValue = GetRoutingRuleValue(routingRule, user, roles);
            return ruleValue;
        }

        public bool GetRoutingValue(RoutingRule routingRule, string user, List<string> roles)
        {
            return GetRoutingRuleValue(routingRule, user, roles);
        }
        #region RoutingRule Cores

        private bool GetRoutingRuleValue(RoutingRule routingRule, string user, List<string> roles)
        {
            if (string.IsNullOrEmpty(routingRule.RuleName))
                return true;

            bool overrideAnd = GetRoutingOverrideAndValue(routingRule, user, roles);
            bool overrideOr = GetRoutingOverrideOrValue(routingRule, user, roles);
            bool overrideOrder = GetRoutingOverrideOrderValue(routingRule, user, roles);

            if (overrideAnd == true)
                return false;
            else if (overrideOr == true)
                return false;
            else if (overrideOrder == true)
                return false;


            bool routingAnd = GetRoutingAndValue(routingRule, roles, user);
            bool routingOr = GetRoutingOrValue(routingRule, roles, user);
            bool routingOrder = GetRoutingOrderValue(routingRule, roles, user);
            if (routingAnd == true)
                return true;
            else if (routingOr == true)
                return true;
            else if (routingOrder == true)
                return true;
            else
                return false;
        }

        private bool GetRoutingAndValue(RoutingRule routingRule, List<string> roles, string user)
        {
            RoutingCondition condition = routingRule.RoutingConditions.Where(r => r.ConditionTye.ToLower().Equals("and")).FirstOrDefault();
            if (condition == null)
                return false;
            bool andFlag = true;
            bool inRole = true;
            foreach (var condiRole in condition.Roles)
            {
                if (!roles.Contains(condiRole))
                {
                    inRole = false;
                    break;
                }
            }
            if (!inRole)
            {
                return false;
            }

            bool inWorkflowStates = false;
            var workflowState = new WorkflowManager().GetWorkflowInstance(routingRule.WorkflowInstanceId).StateName;
            if (condition.WorkflowStates.Contains(workflowState))
                inWorkflowStates = true;
            if (inWorkflowStates)
            {
                return true;
            }

            bool inUserState = true;
            var dbAuditStateUsers = new WorkflowManager().GetWorkflowInstanceAuditTrails(routingRule.WorkflowInstanceId)
                                             .Select(w => new { w.StateName, w.LastModifiedBy, }).ToList();
            foreach (var userState in condition.UserStates)
                {
                    foreach (var dbAuditStateUser in dbAuditStateUsers)
                    {
                        if (dbAuditStateUser.StateName.Equals(userState) && dbAuditStateUser.LastModifiedBy.Equals(user))
                            continue;
                        else
                        {
                            inUserState = false;
                            break;
                        }
                    }
                }
            
            if (!inUserState)
                return false;
            return andFlag;
        }
        private bool GetRoutingOrValue(RoutingRule routingRule, List<string> roles, string user)
        {
            RoutingCondition condition = routingRule.RoutingConditions.Where(r => r.ConditionTye.ToLower().Equals("or")).FirstOrDefault();
            if (condition == null)
                return false;

            bool inRole = false;
            foreach (var condiRole in condition.Roles)
            {
                if (roles.Contains(condiRole))
                {
                    inRole = true;
                    break;
                }
            }

            bool inWorkflowStates = false;
            var workflowState = new WorkflowManager().GetWorkflowInstance(routingRule.WorkflowInstanceId).StateName;
            if (condition.WorkflowStates.Contains(workflowState))
                inWorkflowStates = true;

            bool inUserState = false;
            var dbAuditStateUsers = new WorkflowManager().GetWorkflowInstanceAuditTrails(routingRule.WorkflowInstanceId)
                                             .Select(w => new { w.StateName, w.LastModifiedBy, }).ToList();
            foreach (var userState in condition.UserStates)
            {
                foreach (var dbAuditStateUser in dbAuditStateUsers)
                {
                    if (dbAuditStateUser.StateName.Equals(userState) && dbAuditStateUser.LastModifiedBy.Equals(user))
                    {
                        inUserState = true;
                        break;
                    }
                }
            }

            bool orFlag;
            if (inRole || inWorkflowStates || inUserState)
                orFlag = true;
            else
                orFlag = false;

            return orFlag;
        }
        private bool GetRoutingOrderValue(RoutingRule routingRule, List<string> roles, string user)
        {
            RoutingCondition condition = routingRule.OverrideConditions.Where(r => r.ConditionTye.ToLower().Equals("order")).FirstOrDefault();
            if (condition == null)
                return false;

            bool inUserState = false;
            var dbAuditStateUsers = new WorkflowManager().GetWorkflowInstanceAuditTrails(routingRule.WorkflowInstanceId)
                                             .Select(w => new { w.StateName, w.LastModifiedBy, }).ToList();
            condition.UserStates.Reverse();
            foreach (var userState in condition.UserStates)
            {
                foreach (var dbAuditStateUser in dbAuditStateUsers)
                {
                    if (dbAuditStateUser.StateName.Equals(userState) && dbAuditStateUser.LastModifiedBy.Equals(user))
                    {
                        inUserState = true;
                        break;
                    }
                }
            }
            return inUserState;
        }


        private bool GetRoutingOverrideAndValue(RoutingRule routingRule, string user, List<string> roles)
        {
            RoutingCondition condition = routingRule.OverrideConditions.Where(r => r.ConditionTye.ToLower().Equals("and")).FirstOrDefault();
            if (condition == null)
                return false;
            bool andFlag = true;
            bool inRole = true;
            foreach (var condiRole in condition.Roles)
            {
                if (!roles.Contains(condiRole))
                {
                    inRole = false;
                    break;
                }
            }
            if (!inRole)
            {
                return false;
            }

            bool inWorkflowStates = false;
            var workflowState = new WorkflowManager().GetWorkflowInstance(routingRule.WorkflowInstanceId).StateName;
            if (condition.WorkflowStates.Contains(workflowState))
                inWorkflowStates = true;
            if (inWorkflowStates)
            {
                return true;
            }

            bool inUserState = true;
            var dbAuditStateUsers = new WorkflowManager().GetWorkflowInstanceAuditTrails(routingRule.WorkflowInstanceId)
                                             .Select(w => new { w.StateName, w.LastModifiedBy, }).ToList();
            foreach (var userState in condition.UserStates)
            {
                foreach (var dbAuditStateUser in dbAuditStateUsers)
                {
                    if (dbAuditStateUser.StateName.Equals(userState) && dbAuditStateUser.LastModifiedBy.Equals(user))
                        continue;
                    else
                    {
                        inUserState = false;
                        break;
                    }
                }
            }
            if (!inUserState)
                return false;
            return andFlag;
        }
        private bool GetRoutingOverrideOrValue(RoutingRule routingRule, string user, List<string> roles)
        {
            RoutingCondition condition = routingRule.OverrideConditions.Where(r => r.ConditionTye.ToLower().Equals("or")).FirstOrDefault();
            if (condition == null)
                return false;
            bool inRole = false;
            foreach (var condiRole in condition.Roles)
            {
                if (roles.Contains(condiRole))
                {
                    inRole = true;
                    break;
                }
            }

            bool inWorkflowStates = false;
            var workflowState = new WorkflowManager().GetWorkflowInstance(routingRule.WorkflowInstanceId).StateName;
            if (condition.WorkflowStates.Contains(workflowState))
                inWorkflowStates = true;


            bool inUserState = false;
            var dbAuditStateUsers = new WorkflowManager().GetWorkflowInstanceAuditTrails(routingRule.WorkflowInstanceId)
                                             .Select(w => new { w.StateName, w.LastModifiedBy, }).ToList();
            foreach (var userState in condition.UserStates)
            {
                foreach (var dbAuditStateUser in dbAuditStateUsers)
                {
                    if (dbAuditStateUser.StateName.Equals(userState) && dbAuditStateUser.LastModifiedBy.Equals(user))
                    {
                        inUserState = true;
                        break;
                    }
                }
            }

            bool orFlag;
            if (inRole || inWorkflowStates || inUserState)
                orFlag = true;
            else
                orFlag = false;

            return orFlag;
        }
        private bool GetRoutingOverrideOrderValue(RoutingRule routingRule, string user, List<string> roles)
        {
            RoutingCondition condition = routingRule.OverrideConditions.Where(r => r.ConditionTye.ToLower().Equals("order")).FirstOrDefault();
            if (condition == null)
                return false;

            bool inUserState = false;
            var dbAuditStateUsers = new WorkflowManager().GetWorkflowInstanceAuditTrails(routingRule.WorkflowInstanceId)
                                                         .Select(w => new { w.StateName, w.LastModifiedBy, }).ToList();
            //var workflowstate = new WorkflowManager().GetWorkflowInstance(routingRule.WorkflowInstanceId);
            //dbAuditStateUsers.Add(new { workflowstate.StateName, workflowstate.LastModifiedBy });

            var lastActionStateInfo = dbAuditStateUsers.Select(s => new { s.StateName, s.LastModifiedBy }).LastOrDefault();
            if (lastActionStateInfo != null)
            {
                condition.UserStates.Reverse();
                foreach (var userStateInfo in condition.UserStates)
                {
                    if (lastActionStateInfo.StateName.Equals(userStateInfo.StateName) &&
                        lastActionStateInfo.LastModifiedBy.Equals(user) &&
                        userStateInfo.PickLastRequestInitiator == true)
                    {
                        inUserState = true;
                        break;
                    }
                }
            }
            return inUserState;
        }
        #endregion
    }
}
